// Copyright (c) 2013, Tencent Inc.
// All rights reserved.
//
// Author: hankhuang (hankhuang@tencent.com)
// 

#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>
#include <map>
#include <sstream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>

#include "app_platform/intf_service.h"
#include "app_platform/cntl_info.h"
#include "app_platform/cgi_stub_cntl.h"

#include "c2cent/library/log/CWTimeElf3.h"
#include "c2cplatform/library/string/string_helper.h"
#include "c2cplatform/library/xml/xml_cfg_loader.h"
#include "c2cplatform/library/the3/itil/c2c_attr.h"
#include "c2c_msgqueue_v5/mqconsumer5.h"
#include "c2cplatform/library/the3/tconv/tconv_g2u.h"

#include "libc2c/c2c_errno_define.h"
#include "erp/gis/comm/erp_gis_define.h"
#include "erp/gis/ao/giscoreao_stub4web_allinone.h"
//易迅网站侧购物车Msgq Do头文件
#include "icson/deal/shoppingcart/bo/shoppingcartmsgqobject_deal.h"
#include "icson/deal/shoppingcart/bo/shoppingcartproduct_deal.h"
#include "b2b2c/icson_recvaddr/ao/icsonrecvaddressao_stub4web_allinone.h"
#include "b2b2c/address_mapping/ao/addressmappingao_stub4web_allinone.h"

using std::map;
using std::string;
using std::stringstream;
using std::cout;
using std::endl;
using std::vector;
using namespace c2cent::library::wwlog;
using namespace c2cent::library::wtimeelf;
using namespace c2cplatform::library::string;

using namespace icson::deal::shoppingcart::bo;
using namespace erp::gis::comm;

static const char* DAEMON_NAME = "daemon_gis_useraddr";
static const char* DAEMON_CONFIG_FILE = "daemon_gis_useraddr.conf";
//static const uint32_t TEST_ROUTE_KEY = 1800002399;
static const uint32_t SLEEP_TIME  = 50000;					//默认休眠时间
static const uint32_t DEFAULT_PROCESS_NUM = 1;            	//默认进程数目
static const uint32_t DEFAULT_DEAL_MSGQ_TOPIC_ID = 7466; 		//MsgQ消息的默认topic id
static const uint32_t DEFAULT_DEAL_MSGQ_CONSUMER_ID = 70275;	//MsgQ消息的默认consumer id

bool g_bRunFlag = true;
uint32_t g_dwIsCanLogRemote;
uint32_t g_dwLogSwitch;

string 	 g_strDaeName = "";
uint32_t g_dwSleepTime = 0;
uint32_t g_dwMaxProcessNum;
uint32_t g_dwProcessNo = 0;

CMqConsumer g_oMsgqConsumer;
uint32_t g_dwConsumerId = 0;
uint32_t g_dwTopicId = 0;

enum ITIL_DAEMON_GIS_USERADDR{
	ITIL_GIS_DAEMON_PROCESS_SHIPCART_MSG_REQ = 647388,
	ITIL_GIS_DAEMON_PROCESS_SHIPCART_MSG_ERR = 647389,

	ITIL_GIS_DAEMON_MERGE_COORDINATE_REQ = 647392,
	ITIL_GIS_DAEMON_MERGE_COORDINATE_ERR = 647393,
	ITIL_GIS_DAEMON_MERGE_COORDINATE_SLW = 647394,
};

void LoadConfig(const string& strConfigFile);
uint32_t DoWork();
uint32_t Process();

#define PROCESS_FUNC_PART

int Init(const string& strConfigFile = "")
{
	int iRetCode = 0;
	LoadConfig(strConfigFile);

	iRetCode = C2C_WWLOG->QuickInitForAPP2(g_strDaeName.c_str(),
				C2CWW_LOG_APP_FILE_PATH, g_dwIsCanLogRemote, g_dwLogSwitch);
	if (iRetCode)
	{
		cout << "C2C_WWLOG Init Fail, iRetCode:" << iRetCode << endl;
		return iRetCode;
	}
	C2C_WW_LOG("C2C_WWLOG->QuickInitForAPP OK, LogSwitch[0x%x]", g_dwLogSwitch);


	iRetCode = CCGI_STUB_CNTL->Initialize();
    if(iRetCode)
    {
		cout << "CCGI_STUB_CNTL Init Fail, iRetCode:" << iRetCode << endl;
        C2C_WW_LOG_ERR(iRetCode, "CCGI_STUB_CNTL initialize failed");
        return iRetCode;
    }
    CCGI_STUB_CNTL->setTimeout(3);
	C2C_WW_LOG("CCGI_STUB_CNTL Init OK");

	//初始化进程计数器
	g_dwProcessNo = 1;

	//初始化MsgQ consumer
	iRetCode = g_oMsgqConsumer.Init(g_dwConsumerId, 0, 0, 0, g_strDaeName);
	if(0 != iRetCode)
	{
		C2C_WW_LOG_ERR(iRetCode, "MsgqConsumer.Init failed! Errmsg[%s]", g_oMsgqConsumer.GetLastErrMsg());
		cout << "MsgqConsumer.Init failed Errmsg:" << g_oMsgqConsumer.GetLastErrMsg() << endl;
		return iRetCode;
	}
	C2C_WW_LOG("MsgqConsumer.Init done, TopicId[%u] ConsumerId[%u]", g_dwTopicId, g_dwConsumerId);

    return iRetCode;
}

inline string GbkToUtf8(const std::string& srtGbk)
{
	string strUtf8 = "";
	char szBuf[1024];
	size_t dwLen = srtGbk.length()*2;
	int iErrNum = tconv_gbk2utf8(srtGbk.c_str(), srtGbk.length(), szBuf, &dwLen);
	if(iErrNum == 0)
		strUtf8 = szBuf;
	else
		strUtf8 = srtGbk;

	return strUtf8;
}

bool SortByTime(const b2b2c::icson_recvaddr::po::CIcsonRecvAddrPo& oAddr1,
			const b2b2c::icson_recvaddr::po::CIcsonRecvAddrPo& oAddr2)
{
	bool bResult = false;
	if(oAddr1.GetLastUseTime() > oAddr2.GetLastUseTime())
	{
		bResult = true;
	}else if (oAddr1.GetLastUseTime() < oAddr2.GetLastUseTime())
	{
		bResult = false;
	}else
	{
		bResult = (oAddr1.GetUpdateTime() > oAddr2.GetUpdateTime());
	}

	return bResult;
}

uint32_t RecvMsg(CShoppingcartMsgqObject& oMsgqShopcart)
{
	uint32_t dwRet = 0;

	string strMsg = "";
	uint32_t dwRetSeq = 0;
	uint32_t dwMsgOrigTopicId = 0;
	uint32_t dwMsgOrigCliId = 0;
	uint32_t dwMsgOrigSeq = 0;

	//接收msgq通知
	int32_t iRetCode = g_oMsgqConsumer.SingleRecvMsg(strMsg, dwRetSeq, dwMsgOrigTopicId, dwMsgOrigCliId, dwMsgOrigSeq);
	//没有消息则继续循环等待
	if(iRetCode != 0)
	{
		dwRet = ERR_APP_PARAM_INVALID;
		usleep(g_dwSleepTime);
		return dwRet;
	}

	C2C_WW_LOG("SingleRecvMsg, length[%u] RetSeq[%u] MsgTopicId[%u] MsgCliId[%u] MsgSeq[%u]",
			(uint32_t)strMsg.length(), dwRetSeq, dwMsgOrigTopicId, dwMsgOrigCliId, dwMsgOrigSeq);
	if (dwMsgOrigTopicId != g_dwTopicId)
	{
		dwRet = ERR_APP_PARAM_INVALID;
		return dwRet;
	}

//	C2C_WW_LOG_DEBUG(" oMsgqShopcart.Serialize Start, len[%u] strMsg[%s]", strMsg.length(), strMsg.c_str());
	CByteStreamNetwork bs((char *)strMsg.c_str(), strMsg.length());
	bs.isStoring(false);
	oMsgqShopcart.Serialize(bs);

	//序列化失败
	if(!bs.isGood())
	{
		dwRet = ERR_APP_PARAM_INVALID;
		C2C_WW_LOG_ERR(dwRet, " Serialize Failed. TopicId[%u] MsgSeq[%u] MsgSize[%u]",
				g_dwTopicId, dwMsgOrigSeq, (uint32_t)strMsg.length());
	}

	return dwRet;
}

uint32_t ProcessMsg(CShoppingcartMsgqObject& oMsgqShopcart)
{
	uint32_t dwRet = 0;
	CItilGroup oItilGroup(ITIL_GIS_DAEMON_PROCESS_SHIPCART_MSG_REQ,
			ITIL_GIS_DAEMON_PROCESS_SHIPCART_MSG_ERR, 0, 0, 0);
	C2C_TIME_ELF_ITIL oWTimeElf(C2C_WWLOG, __FUNCTION__, (int*)&dwRet, &oItilGroup);

    uint64_t ddwUid = oMsgqShopcart.GetUserId();
    if(ddwUid == 0)
    	ddwUid = oMsgqShopcart.GetCookieUid();
    C2C_WW_LOG("Recv Shopcart Msg, Uid[%lu] UserId[%lu] CookieUid[%lu] UserIp[%s] Shopcart.size[%u] AddSuccess[%u]",
    		ddwUid, oMsgqShopcart.GetUserId(), oMsgqShopcart.GetCookieUid(), oMsgqShopcart.GetUserIp().c_str(),
    		(uint32_t)oMsgqShopcart.GetProductList().size(),(uint32_t)oMsgqShopcart.GetIsAddSuccess());

    if(ddwUid == 0 || oMsgqShopcart.GetIsAddSuccess() != 1)
    {	//uid无效或添加商品到购物车失败
    	return dwRet;
    }

	CCntlInfo oCntlInfo;
	oCntlInfo.setOperatorUin(ddwUid);
	oCntlInfo.setRouteKey(ddwUid);
	const std::string strMachineKey = __FUNCTION__;
	const std::string& strSource = g_strDaeName;
	uint32_t dwSceneId =0;
	std::string strInReserve = "", strErrMsg = "", strOutReserve = "";

    //查询用户的常用地址列表
    b2b2c::icson_recvaddr::po::CIcsonRecvAddrPoList oRecvAddrList;
    b2b2c::icson_recvaddr::ao::CIcsonRecvAddressAoStub4Web oAddrStub;
    dwRet = oAddrStub.GetIcsonRecvAddr(oCntlInfo, strMachineKey, strSource,
    				dwSceneId, ddwUid, strInReserve,
    				oRecvAddrList, strErrMsg, strOutReserve);
    vector<b2b2c::icson_recvaddr::po::CIcsonRecvAddrPo>& vecRecvAddr =
    				oRecvAddrList.GetIcsonRecvAddrPoList();
    if(dwRet || vecRecvAddr.size() == 0)
    {
		C2C_WW_LOG_ERR(dwRet, "GetIcsonRecvAddr Fail Or AddrList Is Empty, ErrMsg[%s][%s] Uid[%lu] AddrList.size[%u]",
				strErrMsg.c_str(), oAddrStub.GetLastErrMsg(), ddwUid, (uint32_t)vecRecvAddr.size());
		return dwRet;
    }
    C2C_WW_LOG("GetIcsonRecvAddr Done, Uid[%lu] AddrList.size[%u]", ddwUid, (uint32_t)vecRecvAddr.size());

    if(vecRecvAddr.size() >= 100)
    {	//炒货商用户
    	C2C_WW_LOG_ERR(dwRet, " Invalid User Addrress List, Uid[%lu] AddrList.size[%u]",
    			ddwUid, (uint32_t)vecRecvAddr.size());
    	return dwRet;
    }

    //对收货地址按使用时间和更新时间排序，
    sort(vecRecvAddr.begin(), vecRecvAddr.end(), SortByTime);

    //调用AoGisCore.GetMergeCoordinate，刷新用户收货地址的经纬度
    erp::gis::po::CMergedCoordinatesPo mergedCoordinatesPo;
	erp::gis::ao::CGisCoreAoStub4Web oGisStub;

	//查前10个
	uint32_t dwCoorUpdateCnt = 0;
	uint32_t dwRefreshMax = 10, dwCnt = 0;
	for(vector<b2b2c::icson_recvaddr::po::CIcsonRecvAddrPo>::iterator itRecvAddr = vecRecvAddr.begin();
			itRecvAddr != vecRecvAddr.end() && dwCnt < dwRefreshMax; ++itRecvAddr, ++dwCnt)
	{
		//用收货地址的易标ID，查对应国标的省市区名称
		map<uint32_t, uint32_t>& mapDistId = itRecvAddr->GetDistrictIdMap();
		const uint32_t dwDestYbId = mapDistId[AREA_ID_ICSON];
		const uint64_t ddwAddrId = itRecvAddr->GetIcsonAid();

		b2b2c::address_mapping::po::CAreaDetailInfoPo oAreaDetailInfo;
		b2b2c::address_mapping::ao::CAddressMappingAoStub4Web oAddrMapStub;

		dwRet = oAddrMapStub.GetAreaInfoByGbExtId(oCntlInfo, strMachineKey, strSource,
				dwSceneId, 0, dwDestYbId, strInReserve, oAreaDetailInfo,
				strErrMsg, strOutReserve);
		if(dwRet)
		{
			C2C_WW_LOG_ERR(dwRet, "GetAreaInfoByGbExtId Fail, DestYbId[%u] AddrId[%lu] Uid[%lu] ErrMsg[%s][%s] ",
					dwDestYbId, ddwAddrId, ddwUid, strErrMsg.c_str(), oAddrMapStub.GetLastErrMsg());
			continue;
		}
//		C2C_WW_LOG_DEBUG("### DestYbId[%u] AddrId[%lu] UpdateTime[%u] UseTime[%u]",
//				dwDestYbId, ddwAddrId, itRecvAddr->GetUpdateTime(), itRecvAddr->GetLastUseTime());

		const b2b2c::address_mapping::po::CAreaStandardInfoPo&
						gbAreaInfo = oAreaDetailInfo.GetAreaDetailMap()[AREA_ID_GB] ;
		const string& strProvName = gbAreaInfo.GetProvinceName();
		const string& strCityName = gbAreaInfo.GetCityName();
		string strDistName = gbAreaInfo.GetDistrictName();
		string strAddrText = GbkToUtf8(itRecvAddr->GetAddress()); //gbk转utf8

		{
	    CItilGroup oItilGroupGis(ITIL_GIS_DAEMON_MERGE_COORDINATE_REQ,
	    					ITIL_GIS_DAEMON_MERGE_COORDINATE_ERR, 0,
	    					ITIL_GIS_DAEMON_MERGE_COORDINATE_SLW, 100);
	    C2C_TIME_ELF_ITIL oWTimeElfGis(C2C_WWLOG, __FUNCTION__, (int*)&dwRet, &oItilGroupGis);

		dwRet = oGisStub.GetMergeCoordinate(oCntlInfo, strMachineKey, strSource, dwSceneId,
									strProvName, strCityName, strDistName, strAddrText,
									strInReserve, mergedCoordinatesPo,strErrMsg, strOutReserve);
		if(dwRet)
		{
			C2C_WW_LOG_ERR(dwRet, "GetMergeCoordinate Fail, ErrMsg[%s][%s] Uid[%lu]"
					" AddrId[%lu] ProvName[%s] CityName[%s] DistName[%s] AddrText[%s]",
					strErrMsg.c_str(), oGisStub.GetLastErrMsg(), ddwUid, ddwAddrId,
					strProvName.c_str(), strCityName.c_str(), strDistName.c_str(), strAddrText.c_str());
			continue;
		}
		}

		++dwCoorUpdateCnt;
	}

    C2C_WW_LOG("GetMergeCoordinate Done, Uid[%lu] AddrList.size[%u] CoorUpdate.Cnt[%u]",
    		ddwUid, (uint32_t)vecRecvAddr.size(), dwCoorUpdateCnt);
    return dwRet;
}

#define DAEMON_RUN_PART

uint32_t RunLoop()
{
	uint32_t dwRet = 0;

	while(g_bRunFlag)
    {
		CShoppingcartMsgqObject oMsgqShopcart;
		dwRet = RecvMsg(oMsgqShopcart);
		if(dwRet)
			continue;

		ProcessMsg(oMsgqShopcart);
    }

	return dwRet;
}

void LoadConfig(const string& strConfigFile = "")
{
	string sConfigFile = strConfigFile;
	if (sConfigFile == "")
	{
		sConfigFile = DAEMON_CONFIG_FILE;
	}

	string strTempConfig = "";
	c2cplatform::library::xml::CXmlCfgLoader oCfgLoader(sConfigFile.c_str());
	strTempConfig.clear();
	//读取远程日志控制开关
	oCfgLoader.GetConfig("RemoteLog", strTempConfig);
	if (!strTempConfig.empty())
	{
		g_dwIsCanLogRemote = atoi(strTempConfig.c_str());
	}
	else
	{
		g_dwIsCanLogRemote = 0; //默认不打开远程日志
	}

	strTempConfig.clear();
	//读取日志控制配置
	oCfgLoader.GetConfig("LogSwitch", strTempConfig);
	if (!strTempConfig.empty())
	{
		g_dwLogSwitch = atoi(strTempConfig.c_str());
	}
	else
	{
		g_dwLogSwitch = WLOG_ALL_OPEN; //默认打开所有类型日志
	}

	strTempConfig.clear();
	oCfgLoader.GetConfig("DaemonName", strTempConfig);
	if (!strTempConfig.empty())
	{
		g_strDaeName = strTempConfig;
	}
	else
	{
		g_strDaeName = DAEMON_NAME; //默认daemon名称
	}

	strTempConfig.clear();
	oCfgLoader.GetConfig("SleepTime", strTempConfig);
	if (!strTempConfig.empty())
	{
		g_dwSleepTime = atol(strTempConfig.c_str());
	}
	else
	{
		g_dwSleepTime = SLEEP_TIME; //默认值
	}

    //读取consumer id
	oCfgLoader.GetConfig("ConsumerID", strTempConfig);
	if (!strTempConfig.empty())
	{
		g_dwConsumerId = atoi(strTempConfig.c_str());
	}
	else
	{
		g_dwConsumerId = DEFAULT_DEAL_MSGQ_CONSUMER_ID;
	}
	strTempConfig.clear();

    //读取主题id
	oCfgLoader.GetConfig("TopicID", strTempConfig);
	if (!strTempConfig.empty())
	{
		g_dwTopicId = atoi(strTempConfig.c_str());
	}
	else
	{
		g_dwTopicId = DEFAULT_DEAL_MSGQ_TOPIC_ID;
	}
	strTempConfig.clear();

    //读取进程数目
    oCfgLoader.GetConfig("ProcessNum",strTempConfig);
    if (!strTempConfig.empty())
	{
		g_dwMaxProcessNum = atoi(strTempConfig.c_str());
	}
	else
	{
		g_dwMaxProcessNum = DEFAULT_PROCESS_NUM; //设置为默认进程数
	}

    stringstream ssConfig;
    ssConfig <<"ConsumerId[" << g_dwConsumerId << "] \n"
			<<"TopicId[" << g_dwTopicId << "] \n"
			<<"LogRemote[" << g_dwIsCanLogRemote << "] \n"
			<<"LogSwitch[" << g_dwLogSwitch << "] \n"
			<<"DaemonName[" << g_strDaeName << "] \n"
			<<"SleepTime[" << g_dwSleepTime << "] \n"
			<<"ProcessNum[" << g_dwMaxProcessNum << "] \n";

    cout << ssConfig.str() << endl;
}

uint32_t Process()
{
	uint32_t dwResult = 0;
    if (g_dwProcessNo > g_dwMaxProcessNum)
    {
        exit(0);
    }

    int pid = fork();
    if(pid == 0)
	{
        //子进程
		DoWork();
	}
	else if(pid > 0)
	{
        //父进程
        ++g_dwProcessNo;
        Process();
	}
    else
    {
        std::cout << "Error: fork failed" << std::endl;
        exit(1);
    }

    return dwResult;
}

void CatchSignal(int iSignal)
{
    g_bRunFlag = false;
}

uint32_t DoWork()
{
	uint32_t dwRet = 0;
    //设置中断
	signal(SIGTERM, CatchSignal);
    C2C_WW_LOG("[pid:%d]%s(%u/%u) start, ConsumerId:%u TopicId:%u SleepTime:%u LogName:%s RemoteLog:%u LogSwitch:%u",
                getpid(), g_strDaeName.c_str(), g_dwProcessNo, g_dwMaxProcessNum, g_dwConsumerId, g_dwTopicId, g_dwSleepTime,
                g_strDaeName.c_str(), g_dwIsCanLogRemote, g_dwLogSwitch);

    dwRet = RunLoop();
    C2C_WW_LOG("[pid:%d]%s(%u/%u) stop, ret[%u]", getpid(), g_strDaeName.c_str(), g_dwProcessNo, g_dwMaxProcessNum, dwRet);

    return dwRet;
}

int main(int argc, char** argv)
{
	string strConfig = "";
	if(argc >= 2)
	{
		string option = argv[1];
		if(option == "-h" || option == "help")
		{
			printf("USAGE: %s config_file \n", argv[0]);
        	printf("Default config_file: %s.conf \n", argv[0]);
			return 0;
		}

		strConfig = option;
	}
	cout << "Input: ConfigFile[" << strConfig << "]" << endl;

	int dwRet = Init(strConfig);
	if(0 != dwRet)
	{
		C2C_WW_LOG_ERR(dwRet, "Init daemon_gis_useraddr failed!");
		return dwRet;
	}

	dwRet = Process();
	return dwRet;
}
